// C++设计模式　---- 单例模式

// 参考：　[c++中的 单例模式（singleton）和双检测锁（Double-Checked Locking）](https://blog.csdn.net/bdss58/article/details/44813597)

// 一、线程非安全版本

//#define __SINGLETON_ONE_THREAD__
#ifdef __SINGLETON_ONE_THREAD__

//#include <iostream>
//#include <thread>

class Singleton
{
private:
    Singleton() = default;
    //Singleton() { std::cout << std::this_thread::get_id() << " -- Singleton" << std::endl; }
    Singleton(const Singleton& other) = default;

public:
    static Singleton* getInstance();

private:
    static Singleton* m_instance;
};

Singleton* Singleton::m_instance = nullptr;

// 线程非安全版本
Singleton* Singleton::getInstance() {
    if(m_instance == nullptr) {
        m_instance = new Singleton();
    }
    return m_instance;
}
#endif // __SINGLETON_ONE_THREAD__


// 二、加锁版本

// 线程安全版本，但是锁的代价过高

//#define __SINGLETON_LOCK__
#ifdef __SINGLETON_LOCK__
#include <mutex>

class Singleton
{
private:
    Singleton() = default;
    Singleton(const Singleton& other) = default;

public:
    static Singleton* getInstance();

private:
    static Singleton* m_instance;
    static std::mutex m_mutex;
};

Singleton* Singleton::getInstance() {
    std::lock_guard<std::mutex> lock(m_mutex); // Lock lock;
    if(m_instance == nullptr) {
        m_instance = new Singleton();
    }
    return m_instance;
}
#endif  // __SINGLETON_LOCK__


// 三、双检查锁版本

// 双检查锁DCLP--Double Check Lock Pattern，但由于内存读写reorder不安全

//#define __SINGLETON_DCLP__
#ifdef __SINGLETON_DCLP__

#include <iostream>
#include <mutex>
#include <thread>

class Singleton
{
private:
    //Singleton() = default;
    Singleton() { std::cout << std::this_thread::get_id() << " -- Singleton" << std::endl; }
    Singleton(const Singleton& other) = default;

public:
    static Singleton* getInstance();

private:
    static Singleton* m_instance;
    static std::mutex m_mutex;
};

Singleton* Singleton::m_instance = nullptr;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() {
    std::cout << std::this_thread::get_id() << " -- getInstance enter" << std::endl;
    if(m_instance == nullptr) {
        std::lock_guard<std::mutex> lock(m_mutex); //Lock lock;
        if(m_instance == nullptr) {
            m_instance = new Singleton();
        }
    }
    std::cout << std::this_thread::get_id() << " -- getInstance exit" << std::endl;
    return m_instance;
}
#endif // __SINGLETON_DCLP__


// 解决了锁代价过高的问题，线程安全
// 存在问题：内存读写reorder导致双检查锁失效
// 
// m_instance = new Singleton();
//
//  为执行这句话，机器干了三件事：
//
// 1. 分配内存空间                          operator new(sizeof(Singleton));
// 2. 调用构造器，在分配的空间中构造对象        new (m_instance) Singleton;
// 3. 分配的地址给instance                  m_instance = ...
//
// 但是编译器优化后，指令执行的顺序是1-->3-->2，不是期望的1--2-->3。



// 四、双检查锁＋atomic方案

// C#和Java解决机制：volatile关键字，编译时告诉编译器不能对它进行编译优化
// 微软平台添加volatile
// C++11之后实现了跨平台的解决方案（volatile）
// 使用atomic

#define __SINGLETON_ATOMIC__
#ifdef __SINGLETON_ATOMIC__
#include <iostream>
#include <atomic>
#include <mutex>
#include <thread>

class Singleton
{
private:
    //Singleton() = default;
    Singleton() { std::cout << std::this_thread::get_id() << " -- Singleton" << std::endl; }
    Singleton(const Singleton& other) = default;

public:
    static Singleton* getInstance();

private:
    static std::atomic<Singleton*> m_instance;
    static std::mutex m_mutex;
};
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() {
    std::cout << std::this_thread::get_id() << " -- getInstance enter" << std::endl;
    Singleton* tmp = m_instance.load(std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_acquire); // 获取内存fence
    if(tmp == nullptr) {
        std::lock_guard<std::mutex> lock(m_mutex);
        tmp = m_instance.load(std::memory_order_relaxed);
        if(tmp == nullptr) {
            tmp = new Singleton;
            std::atomic_thread_fence(std::memory_order_release); // 释放内存fence
            m_instance.store(tmp, std::memory_order_relaxed);
        }
    }
    std::cout << std::this_thread::get_id() << " -- getInstance exit " << tmp << std::endl;
    return tmp;
}
#endif //__SINGLETON_ATOMIC__ 

// 双检查锁机制，同时解决了内存reorder的问题，C++跨平台解决方案

void fun() {
    Singleton* instance = Singleton::getInstance();
}

int main()
{
    std::thread t1(fun);
    std::thread t2(fun);
    std::thread t3(fun);
    t1.join();
    t2.join();
    t3.join();

    return 0;
}